Check in new file format 'stmsdf', Suunto Trek Manager .sdf files.
authoroliskoli <oliskoli>
Sun, 30 Jul 2006 17:46:56 +0000 (17:46 +0000)
committeroliskoli <oliskoli>
Sun, 30 Jul 2006 17:46:56 +0000 (17:46 +0000)
Makefile.in
stmsdf.c [new file with mode: 0644]
testo
vecs.c
xmldoc/formats/stmsdf.xml [new file with mode: 0644]

index 1438048bb696621788c9fb4d625ed549e4704026..69ef8b60e48f2c35e39254408b90924481667f07 100644 (file)
@@ -53,7 +53,7 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \
        tef_xml.o maggeo.o pathaway.o vitosmt.o gdb.o bcr.o coto.o \
        ignrando.o stmwpp.o msroute.o cst.o nmn4.o mag_pdb.o compegps.o \
        yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o \
-       wbt-200.o
+       wbt-200.o stmsdf.o
 
 FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o \
        reverse_route.o sort.o stackfilter.o trackfilter.o discard.o \
@@ -489,6 +489,8 @@ sort.o: sort.c defs.h config.h queue.h gbtypes.h cet.h cet_util.h \
   inifile.h filterdefs.h
 stackfilter.o: stackfilter.c defs.h config.h queue.h gbtypes.h cet.h \
   cet_util.h inifile.h filterdefs.h
+stmsdf.o: stmsdf.c defs.h config.h queue.h gbtypes.h cet.h cet_util.h \
+  csv_util.h grtcirc.h strptime.h jeeps/gpsmath.h
 stmwpp.o: stmwpp.c defs.h config.h queue.h gbtypes.h cet.h cet_util.h \
   inifile.h csv_util.h
 strptime.o: strptime.c strptime.h
diff --git a/stmsdf.c b/stmsdf.c
new file mode 100644 (file)
index 0000000..ae73113
--- /dev/null
+++ b/stmsdf.c
@@ -0,0 +1,717 @@
+/*
+
+    Support for Suunto Trackmanager SDF format,
+    Copyright (C) 2005-2006 Olaf Klein, o.b.klein@t-online.de
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+*/
+
+/*
+    2006/04/05: initial release (not published in GPSBbabel)
+    2006/07/19: finished reader and writer for type 4,5,28 of ver. 1
+    
+    ToDo: Ascending/Descending
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "defs.h"
+#include "csv_util.h"
+#include "strptime.h"
+#include "jeeps/gpsmath.h"
+#include "grtcirc.h"
+
+#include <time.h>
+
+#define MYNAME "stmsdf"
+
+#define ALT(a) (a->altitude != unknown_alt) ? a->altitude : 0
+
+typedef enum {
+       sdf_unknown,
+       sdf_header,
+       sdf_points,
+       sdf_custom
+} sdf_section_e;
+
+static gbfile *fin, *fout;
+
+typedef struct stmsdf_s {
+} stmsdf_t;
+
+static int lineno;
+static int datum;
+static int datum86;
+static int filetype;
+static route_head *route;
+static queue trackpts;
+static char *rte_name;
+static char *rte_desc;
+
+static waypoint *trkpt_out;
+static route_head *trk_out;
+
+static double trkpt_dist;
+static double minalt, maxalt, maxspeed;
+static double this_distance, all_dist;
+static time_t this_time, all_time;
+static double all_asc, all_desc;
+static int this_index;                 /* from 1 to ... */
+static int all_points;
+static int this_points;
+static int saved_points;
+static time_t start_time;
+static unsigned char this_valid;
+
+#define route_index this_index
+#define track_index this_index
+#define all_route_points all_points
+#define all_track_points all_points
+#define route_points this_points
+#define track_points this_points
+#define saved_track_points saved_points
+#define this_route_valid this_valid
+
+/* placeholders for options */
+
+static char *opt_route_index;
+static int opt_route_index_value;
+
+static
+arglist_t stmsdf_args[] = {
+       {"index", &opt_route_index, "Index of route (if more the one in source)", 
+               "1", ARGTYPE_INT, "1", NULL },
+       ARG_TERMINATOR
+};
+
+
+/* ----------------------------------------------------------- */
+
+static void
+parse_header(char *line)
+{
+       char *str;
+       char *key = NULL;
+       char *prod = NULL;
+       int column = -1;
+       
+       while ((str = csv_lineparse(line, "=", "", lineno)))
+       {
+               line = NULL;
+               column++;
+               
+               switch(column) {
+                       case 0: 
+                               key = xstrdup(str);
+                               break;
+                       case 1: 
+                               if (case_ignore_strcmp(key, "DATUM") == 0) datum = GPS_Lookup_Datum_Index(str);
+                               else if (case_ignore_strcmp(key, "FILEVERSION") == 0) {
+                                       int ver = atoi(str);
+                                       is_fatal( (ver != 1),
+                                               MYNAME ": This version '%d' is not yet supported. Please report!", ver);
+                               }
+                               else if (case_ignore_strcmp(key, "NAME") == 0) rte_name = xstrdup(str);
+                               else if (case_ignore_strcmp(key, "NOTES") == 0) /* ToDo */;
+                               else if (case_ignore_strcmp(key, "SOURCE") == 0) rte_desc = xstrdup(str);
+                               else if (case_ignore_strcmp(key, "TYPE") == 0) {
+                                       filetype = atoi(str);
+                                       switch(filetype) {
+                                               case 4: /* M9 TrackLog (Suunto Sail Manager) */
+                                               case 5: /* route */
+                                               case 28: /* X9 TrackLog (Suunto Trek Manager */
+                                                       break;
+                                                       
+                                               case 78: prod = "S6 SkiChrono";
+                                               case 79: prod = "S6 Skilog";
+                                               
+                                               default:
+                                                       if (prod == NULL) prod = "unknown";
+                                                       fatal(MYNAME ": Unsupported file type (%s, type %d)!\n", prod, filetype);
+                                       }
+                               }
+                               break;
+               }
+       }
+       if (key) xfree(key);
+       
+}
+
+static int
+track_qsort_cb(const void *a, const void *b)
+{
+       const waypoint *wa = *(waypoint **)a;
+       const waypoint *wb = *(waypoint **)b;
+       
+       return wa->creation_time - wb->creation_time;
+}
+
+static void
+finalize_tracks(void)
+{
+       waypoint **list;
+       int count = 0;
+       queue *elem, *tmp;
+       int index;
+       route_head *track = NULL;
+       int trackno = 0;
+       
+       count = 0;
+       QUEUE_FOR_EACH(&trackpts, elem, tmp) { count++; };
+       if (count == 0) return;
+       
+       list = (void *)xmalloc(count * sizeof(*list));
+       
+       index = 0;
+       QUEUE_FOR_EACH(&trackpts, elem, tmp) {
+               list[index] = (waypoint *)elem;
+               dequeue(elem);
+               index++;
+       }
+
+       qsort(list, count, sizeof(*list), track_qsort_cb);
+
+       for (index = 0; index < count; index++) {
+               waypoint *wpt = list[index];
+               if (wpt->centiseconds == 2) {   /* log continued */
+                       track = NULL;
+               }
+               if (track == NULL) {
+                       track = route_head_alloc();
+                       track_add_head(track);
+                       trackno++;
+                       if (rte_name != NULL) {
+                               if (trackno > 1)
+                                       xasprintf(&track->rte_name, "%s (%d)", rte_name, trackno);
+                               else
+                                       track->rte_name = xstrdup(rte_name);
+                       }
+                       if (rte_desc != NULL)
+                               track->rte_desc = xstrdup(rte_desc);
+               }
+               track_add_wpt(track, wpt);
+               if (wpt->centiseconds == 1) { /* log pause */
+                       track = NULL;
+               }
+               wpt->centiseconds = 0;
+       }
+
+       xfree(list);
+}
+
+static void
+parse_point(char *line)
+{
+       char *str;
+       int column = -1;
+       int what = -1;          /* -1 = unknown, 0 = tp, 1 = mp, 2 = wp, 3 = ap  */
+       waypoint *wpt = NULL;
+       char *cx;
+       int hour, min, sec, day, month, year;
+
+       year = hour = -1;
+       
+       while ((str = csv_lineparse(line, ",", "", lineno)))
+       {
+
+               line = NULL;
+               column++;
+               
+               switch(column) {
+                       
+                       case 0: 
+                               if (strcmp(str, "\"TP\"") == 0) {
+                                       what = 0;
+                                       column++;       /* skip name */
+                               }
+                               else if (strcmp(str, "\"MP\"") == 0) what = 1;
+                               else if (strcmp(str, "\"WP\"") == 0) what = 2;
+                               else if (strcmp(str, "\"AP\"") == 0) what = 3;
+                               else {
+                                       warning(MYNAME ": Unknown point type %s at line %d!\n", str, lineno);
+                                       return;
+                               }
+                               wpt = waypt_new();
+                               break;
+                               
+                       case 1:
+                               wpt->shortname = csv_stringclean(str, "\"");
+                               if ((what == 2) || (what == 3)) column += 2;    /* doesn't have date and time */
+                               break;
+                       case 2:
+                               sscanf(str, "%d.%d.%d", &day, &month, &year);
+                               break;
+                       case 3:
+                               while ((cx = strchr(str, '.'))) *cx = ':';
+                               sscanf(str, "%d:%d:%d", &hour, &min, &sec);
+                               break;
+                       case 4:
+                               wpt->latitude = atof(str);
+                               break;
+                       case 5:
+                               wpt->longitude = atof(str);
+                               break;
+                       case 6:
+                               wpt->altitude = atof(str);
+                               break;
+                       case 7:
+                               switch(what) {
+                                       case 0: 
+                                               wpt->speed = atof(str) * 3.6; break;
+                                       case 3: 
+                                               wpt->proximity = atof(str); 
+                                               xasprintf(&wpt->notes, "Alarm point: radius=%s", str);
+                                               break;
+                               }
+                               break;
+                       case 8:
+                               if (what == 0) wpt->course = atof(str);
+                               break;
+                       case 9:
+                       case 10:
+                               break;
+                       case 11:
+                               if (what == 1) wpt->centiseconds = atoi(str); /* memory point type */
+                               break;
+               }
+       }
+       
+       if ((year > -1) && (hour > -1)) {
+               struct tm tm;
+               
+               memset(&tm, 0, sizeof(tm));
+               
+               tm.tm_year = year - 1900;
+               tm.tm_mon = month - 1;
+               tm.tm_mday = day;
+               tm.tm_hour = hour;
+               tm.tm_min = min;
+               tm.tm_sec = sec;
+               
+               wpt->creation_time = mklocaltime(&tm);
+       }
+                       
+       if (datum != datum86) {
+               double ht;
+               GPS_Math_WGS84_To_Known_Datum_M(wpt->latitude, wpt->longitude, 0,
+                       &wpt->latitude, &wpt->longitude, &ht, datum);
+       }
+       
+       switch(what) {
+               case 0:
+               case 1:
+                       ENQUEUE_TAIL(&trackpts, &wpt->Q);
+                       break;
+               case 2:
+               case 3:
+                       if (route == NULL) {
+                               route = route_head_alloc();
+                               route_add_head(route);
+                       }
+                       route_add_wpt(route, wpt);
+                       break;
+       }
+}
+
+/* ----------------------------------------------------------- */
+
+static void
+rd_init(const char *fname)
+{
+       fin = gbfopen(fname, "r", MYNAME);
+       
+       lineno = 0;
+       route = NULL;
+       datum = datum86 = GPS_Lookup_Datum_Index("WGS84");
+       filetype = 28;
+       rte_name = rte_desc = NULL;
+
+       QUEUE_INIT(&trackpts);
+}
+
+static void
+rd_deinit(void)
+{
+       gbfclose(fin);
+       if (rte_name) xfree(rte_name);
+       if (rte_desc) xfree(rte_desc);
+}
+
+static void
+data_read(void)
+{
+       char *buf;
+       sdf_section_e section = sdf_unknown;
+       
+       while ((buf = gbfgetstr(fin)))
+       {
+               char *cin = lrtrim(buf);
+               lineno++;
+               
+               if (*cin == '\0') continue;
+               
+               if (*cin == '[')
+               {
+                       char *cend = strchr(++cin, ']');
+
+                       if (cend != NULL)
+                       {
+                               *cend = '\0';
+                               cin = lrtrim(cin);
+                       }
+                       if ((*cin == '\0') || (cend == NULL))
+                               fatal(MYNAME ": Invalid section header!\n");
+                               
+                       if (case_ignore_strcmp(cin, "HEADER") == 0) section = sdf_header;
+                       else if (case_ignore_strcmp(cin, "POINTS") == 0) section = sdf_points;
+                       else if (case_ignore_strncmp(cin, "CUSTOM", 6) == 0) section = sdf_custom;
+                       else {
+                               warning(MYNAME ": Unknown section \"%s\". Please report.\n", cin);
+                               section = sdf_unknown;
+                       }
+               }
+               else switch(section) 
+               {
+                       case sdf_header: 
+                               parse_header(cin);
+                               break;
+                       case sdf_points: 
+                               parse_point(cin);
+                               break;
+                       case sdf_custom: 
+                       case sdf_unknown: break;
+               }
+       }
+       finalize_tracks();      /* memory points can be at the end of all trackpoints */
+}
+
+
+static void
+calculate(const waypoint *wpt, double *dist, double *speed, double *course, 
+         double *asc, double *desc)
+{
+       if (trkpt_out != NULL) {
+               
+               time_t time;
+               
+               *course = 360 - DEG(heading(
+                       RAD(trkpt_out->latitude), RAD(trkpt_out->longitude),
+                       RAD(wpt->latitude), RAD(wpt->longitude)));
+               if (*course >= 360) *course -= 360;
+
+               *dist = radtometers(gcdist(
+                       RAD(trkpt_out->latitude), RAD(trkpt_out->longitude), 
+                       RAD(wpt->latitude), RAD(wpt->longitude)));
+               if (*dist < 0.1) *dist = 0;     /* calc. diffs on 32- and 64-bit hosts */
+               
+               time = wpt->creation_time - trkpt_out->creation_time;
+               if (time == 0) 
+                       *speed = 0;
+               else 
+                       *speed = *dist / (double)time;
+                       
+               if (asc && desc && (trkpt_out->altitude != unknown_alt) && (wpt->altitude != unknown_alt)) {
+                       double dh = wpt->altitude - trkpt_out->altitude;
+                       if (dh > 0)
+                               *asc += dh;
+                       else
+                               *desc -= dh;
+               }
+               
+       }
+       else {
+               *speed = 0;
+               *dist = 0;
+               *course = 0;
+               if (asc) *asc = 0;
+               if (desc) *desc = 0;
+       }
+       if (wpt->speed != unknown_speed) *speed = wpt->speed / 3.6;     /* -> meters per second */
+       if (wpt->course != unknown_course) *course = wpt->course;
+}
+
+/* pre-calculation callbacks */
+
+static void 
+any_hdr_calc_cb(const route_head *trk)
+{
+       
+       trkpt_out = NULL;
+       this_distance = 0;
+       this_time = 0;
+       this_points = 0;
+       
+       this_index++;
+       this_valid = ((opt_route_index_value < 1) || (opt_route_index_value == this_index));
+       if (! this_valid) return;
+       
+       if (!rte_name && trk->rte_name) {
+               rte_name = trk->rte_name;
+               rte_desc = trk->rte_desc;
+       }
+       
+       trk_out = (route_head *)trk;
+}
+
+static void 
+any_waypt_calc_cb(const waypoint *wpt)
+{
+       double speed, course, dist;
+       
+       /* we can only write ONE route */
+       if (! this_valid) return;
+       
+       if ((all_points == 0) && (this_points == 0)) start_time = wpt->creation_time;
+
+       this_points++;
+               
+       if ((wpt->altitude != unknown_alt) && (wpt->altitude < minalt)) minalt = wpt->altitude;
+       if ((wpt->altitude != unknown_alt) && (wpt->altitude > maxalt)) maxalt = wpt->altitude;
+       calculate(wpt, &dist, &speed, &course, &all_asc, &all_desc);
+       if (speed > maxspeed) maxspeed = speed;
+
+       this_distance = this_distance + dist;
+       if (trkpt_out != NULL) 
+               this_time += (wpt->creation_time - trkpt_out->creation_time);
+                       
+       trkpt_out = (waypoint *)wpt;
+}
+
+static void 
+any_tlr_calc_cb(const route_head *trk)
+{
+       if (! this_valid) return;
+       
+       all_dist += this_distance;
+       all_time += this_time;
+       all_points += this_points;
+}
+
+/* write callbacks */
+
+static void 
+track_disp_hdr_cb(const route_head *trk)
+{
+       track_index++;
+       track_points = 0;
+       trk_out = (route_head *)trk;
+       trkpt_out = NULL;
+}
+
+
+static void 
+track_disp_wpt_cb(const waypoint *wpt)
+{
+       struct tm tm;
+       char tbuf[32];
+       double course, speed, dist;
+       int flag = 0;
+
+       track_points++;
+       all_track_points++;
+       
+       tm = *localtime(&wpt->creation_time);
+       strftime(tbuf, sizeof(tbuf), "%d.%m.%Y,%H:%M.%S", &tm);
+       
+       calculate(wpt, &dist, &speed, &course, NULL, NULL);
+       trkpt_dist = trkpt_dist + dist;
+               
+       if (track_points == trk_out->rte_waypt_ct) {    /* I'm the last in that list */
+               if (all_track_points != saved_track_points) {   /* but not the overall latest */
+                       flag = 1;
+               }
+       }
+       else if (track_points == 1) {                   /* I'm the first in that list */
+               if (all_track_points > 1) {                     /* but not the first ever seen */
+                       flag = 2;
+               }
+       }
+
+       if (flag == 1) {
+               char *name = wpt->shortname;
+               if (name == NULL) name = "Log paused";
+               gbfprintf(fout, "\"MP\",\"%s\"", name);
+       }
+       else if (flag == 2) {
+               char *name = wpt->shortname;
+               if (name == NULL) name = "Log continued";
+               gbfprintf(fout, "\"MP\",\"%s\"", name);
+       }
+       else
+               gbfprintf(fout, "\"TP\"");
+
+       gbfprintf(fout, ",%s,%.6lf,%.6lf,%.f,%.2f", 
+               tbuf,
+               wpt->latitude, wpt->longitude, ALT(wpt), speed);
+       if (flag) 
+               gbfprintf(fout, ",0,0,%d", flag);       /* press, temperature, memory point type */
+       else
+               gbfprintf(fout, ",%.1f", course);
+               
+       if (trkpt_dist != 0)
+               gbfprintf(fout, ",%.6f\n", trkpt_dist);
+       else
+               gbfprintf(fout, ",0\n");
+
+       trkpt_out = (waypoint *)wpt;
+}
+
+static void 
+track_disp_tlr_cb(const route_head *rte)
+{
+       trkpt_out = NULL;
+}
+
+static void
+route_disp_hdr_cb(const route_head *rte)
+{
+       route_index++;
+       this_route_valid = ((opt_route_index_value < 1) || (opt_route_index_value == track_index));
+}
+
+static void
+route_disp_wpt_cb(const waypoint *wpt)
+{
+       if (this_route_valid) {
+               gbfprintf(fout, "\"WP\",\"%s\",%.8lf,%.8lf,%.f\n",
+                       wpt->shortname, wpt->latitude, wpt->longitude, ALT(wpt));
+       }
+}
+
+static void
+track_disp_custom_cb(const waypoint *wpt)
+{
+       if (wpt->creation_time && (wpt->altitude != unknown_alt)) {
+               gbfprintf(fout, "%lu,%.f\n", wpt->creation_time - start_time, wpt->altitude);
+       }
+}
+
+static void
+wr_init(const char *fname)
+{
+       fout = gbfopen(fname, "w", MYNAME);
+}
+
+static void
+wr_deinit(void)
+{
+       gbfclose(fout);
+}
+
+static void
+data_write(void)
+{
+       gbfprintf(fout, "[HEADER]\n");
+       gbfprintf(fout, "FILEVERSION=1\n");
+       gbfprintf(fout, "SOURCE=FILE\n");
+       gbfprintf(fout, "DATUM=WGS84\n");
+       
+       rte_name = NULL;
+       rte_desc = NULL;
+       trkpt_out = NULL;
+       opt_route_index_value = -1;     /* take all tracks from data pool */
+       track_index = 0;
+       minalt = -unknown_alt;
+       maxalt = unknown_alt;
+       maxspeed = 0;
+       all_dist = 0;
+       all_time = 0;
+       all_asc = 0;
+       all_desc = 0;
+       all_points = 0;
+       start_time = 0;
+       
+       
+       switch(global_opts.objective)
+       {
+               case rtedata:
+                       gbfprintf(fout, "TYPE=5\n");
+
+                       opt_route_index_value = atoi(opt_route_index);
+                       route_disp_all(any_hdr_calc_cb, any_tlr_calc_cb, any_waypt_calc_cb);
+                       gbfprintf(fout, "DISTANCE=%.f\n", all_dist);
+                       if (rte_name) gbfprintf(fout, "NAME=%s\n", rte_name);
+                       gbfprintf(fout, "[POINTS]\n");
+                       if (route_points > 0) {
+                               track_index = 0;
+                               route_disp_all(route_disp_hdr_cb, NULL, route_disp_wpt_cb);
+                       }
+                       break;
+
+               case wptdata:
+               case trkdata:
+                       gbfprintf(fout, "TYPE=28\n");
+                       
+                       track_disp_all(any_hdr_calc_cb, any_tlr_calc_cb, any_waypt_calc_cb);
+                       if (all_track_points > 0) {
+                               if (rte_name) gbfprintf(fout, "NAME=%s\n", rte_name);
+                               if (minalt != -unknown_alt) gbfprintf(fout, "MINALT=%.f\n", minalt);
+                               if (maxalt != unknown_alt) gbfprintf(fout, "MAXALT=%.f\n", maxalt);
+                               gbfprintf(fout, "MAXSPEED=%.2f\n", maxspeed);
+                               gbfprintf(fout, "DISTANCE=%.f\n", all_dist);
+                               gbfprintf(fout, "DURATION=%lu\n", all_time);
+//                             gbfprintf(fout, "TOTASC=%.f\n", all_asc);
+//                             gbfprintf(fout, "TOTDSC=%.f\n", all_desc);
+                               if (start_time) {
+                                       struct tm tm;
+                                       char tbuf[32];
+                                       
+                                       tm = *localtime(&start_time);
+                                       strftime(tbuf, sizeof(tbuf), "%d.%m.%Y %H:%M.%S", &tm);
+                                       gbfprintf(fout, "DATE=%s\n", tbuf);
+                               }
+                               if (all_time) gbfprintf(fout, "AVGSPEED=%.2f\n", all_dist / (double)all_time);
+                       }
+                       gbfprintf(fout, "[POINTS]\n");
+                       if (all_track_points > 0) {
+                       
+                               trkpt_dist = 0;
+                               saved_track_points = all_track_points;
+                               all_track_points = 0;
+                               track_disp_all(track_disp_hdr_cb, track_disp_tlr_cb, track_disp_wpt_cb);
+                               
+                               if (start_time) {
+                                       gbfprintf(fout, "[CUSTOM1]\n");
+                                       track_index = 0;
+                                       track_disp_all(NULL, NULL, track_disp_custom_cb);
+                               }
+                       }
+                       break;
+       }
+}
+
+/* ------------------------------------------------------------------ */
+
+ff_vecs_t stmsdf_vecs = {
+       ff_type_file,
+       { ff_cap_none, 
+         ff_cap_read | ff_cap_write, 
+         ff_cap_read | ff_cap_write },
+       rd_init,
+       wr_init,
+       rd_deinit,
+       wr_deinit,
+       data_read,
+       data_write,
+       NULL,
+       stmsdf_args,
+       CET_CHARSET_MS_ANSI, 0  /* CET-REVIEW */
+};
+
+/* ================================================================== */
diff --git a/testo b/testo
index bcf49edda6415ba82181607f62d7c48068ba5bf2..a27baef727836457620e258d1d8912df1feb8b71 100755 (executable)
--- a/testo
+++ b/testo
@@ -1090,5 +1090,15 @@ rm -f ${TMPDIR}/sportsim*
 ${PNAME} -i gpx -f ${REFERENCE}/expertgps.gpx -x nuketypes,waypoints,routes -o sportsim -F ${TMPDIR}/sportsim.txt
 compare ${TMPDIR}/sportsim.txt ${REFERENCE}/track/sportsim-sample.txt
 
+#
+# Suunto SDF
+#
+rm -f ${TMPDIR}/stmsdf*
+${PNAME} -i gpx -f ${REFERENCE}/expertgps.gpx -r -o stmsdf -F ${TMPDIR}/stmsdf-route.sdf -t -o stmsdf -F ${TMPDIR}/stmsdf-track.sdf
+compare ${TMPDIR}/stmsdf-route.sdf ${REFERENCE}/route/stmsdf-route.sdf
+compare ${TMPDIR}/stmsdf-track.sdf ${REFERENCE}/track/stmsdf-track.sdf
+${PNAME} -i stmsdf -f ${TMPDIR}/stmsdf-route.sdf -f ${TMPDIR}/stmsdf-track.sdf -o gpx -F ${TMPDIR}/stmsdf-example.gpx
+compare ${TMPDIR}/stmsdf-example.gpx ${REFERENCE}/stmsdf-example.gpx
+
 
 exit 0
diff --git a/vecs.c b/vecs.c
index 0f39461bfac303408a2270c73fe2a16c0909e95e..fd96d6f4f00541d95ba1d62d64ba6186052bffb3 100644 (file)
--- a/vecs.c
+++ b/vecs.c
@@ -91,6 +91,7 @@ extern ff_vecs_t psp_vecs;
 extern ff_vecs_t quovadis_vecs;
 extern ff_vecs_t saroute_vecs;
 extern ff_vecs_t shape_vecs;
+extern ff_vecs_t stmsdf_vecs;
 extern ff_vecs_t stmwpp_vecs;
 extern ff_vecs_t tef_xml_vecs;
 extern ff_vecs_t text_vecs;
@@ -502,6 +503,12 @@ vecs_t vec_list[] = {
                "IGN Rando track files",
                "rdn"
        },
+       {
+               &stmsdf_vecs,
+               "stmsdf",
+               "Suunto Trek Manager (STM) .sdf files",
+               "sdf"
+       },
        {
                &stmwpp_vecs,
                "stmwpp",
diff --git a/xmldoc/formats/stmsdf.xml b/xmldoc/formats/stmsdf.xml
new file mode 100644 (file)
index 0000000..e01b497
--- /dev/null
@@ -0,0 +1,23 @@
+<para>
+       This format supports the .sdf files from the Suunto product family
+       'Suunto Trek Manager', 'Suunto Ski Manager' and 'Suunto Sail Manager'.
+       The contents of the sdf file depends on the used product and can
+       be one route or one track. Thatswhy when you want to use sdf on the
+       output side you have to use the
+       <link linkend="Route_And_Track_Modes">-r OR the -t</link> option. This will tell
+       GPSBabel which type of data should be written.
+</para>
+<para>
+       Currently we can read the following file types:
+       <simplelist columns="1">
+               <member>4 = M9 TrackLog</member>
+               <member>5 = Route</member>
+               <member>28 = X9 TrackLog</member>
+       </simplelist>
+</para>
+<para>
+       <screen format="linespecific">gpsbabel -i gpx -f some-routes.gpx -r -o stmsdf,index=3 -F single-route.sdf</screen>
+</para>
+<para>
+       <ulink url="http://www.suunto.fi">Suunto Website</ulink>
+</para>